﻿
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Reactive.Concurrency;
using System.Windows.Markup;
using System.Xaml;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

using alib;
using alib.Concurrency;
using alib.Debugging;
using alib.Enumerable;
using alib.Reflection;
using alib.Collections.ReadOnly;
using alib.Collections;
using alib.Array;
using alib.Memory;

using agree.configuration;
using agree.schema;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class ChartAwait<A> : m_scalar_await<A, IChartControl>, INotifyCompletion, ICriticalNotifyCompletion
		where A : IActiveObj<IIdentity>
	{
		public ChartAwait(IRtParent parent, A items)
			: base(parent, items)
		{
			this.rga = Collection<Action>.None;
		}

		Action[] rga;

		public ChartAwait<A> GetAwaiter() { return this; }

		public ChartAwait<A> GetResult()
		{
			if (!IsCompleted)
				throw new Exception();
			return this;
		}

		void INotifyCompletion.OnCompleted(Action etc)
		{
			((ICriticalNotifyCompletion)this).UnsafeOnCompleted(etc);
		}

		void ICriticalNotifyCompletion.UnsafeOnCompleted(Action etc)
		{
			var a = this.rga;
			do
				if (a == null)
				{
					etc();
					return;
				}
			while (a != (a = Interlocked.CompareExchange(ref this.rga, arr.Append(a, etc), a)));
		}

		protected void complete_awaiters()
		{
			var a = this.rga;
			if (a != null && (a = Interlocked.Exchange(ref this.rga, null)) != null)
			{
				for (int i = 0; i < a.Length; i++)
					a[i]();
			}
		}

		public override bool IsCompleted { get { return rga == null; } }

		/////////////////////////////////////////////////////////
		/// forced wait for interop with older code
		/// 
		public override void Wait()
		{
			if (!IsCompleted)
				__w().Result.Wait();
		}
		async Task<ChartAwait<A>> __w() { return await this; }
		/// 
		/////////////////////////////////////////////////////////
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class ChartSequencer<W> : ChartAwait<IActiveObj<IChartObj<W>>>
		where W : struct, IProximalContext<W>
	{
		public ChartSequencer(IRtParent parent, IActiveObj<IChartObj<W>> items)
			: base(parent, items)
		{
			this.next_obj_id = 2;
			this.c_tasks = 1;
			this.c_tasks_max = 1;
			SysObj.BeginApplicationTask();

			this.timings = new Stack<long>();
			this._ts = new _timing_suspension(this);
		}

		public long UnsequencedId()
		{
			return Interlocked.Add(ref next_obj_id, 2);
		}

		protected int c_tasks;
		public int c_tasks_max;
		internal long next_obj_id;
		public long tick_total;
		Stack<long> timings;

		_timing_suspension _ts;
		public _timing_suspension suspend_timing { get { end(); return _ts; } }
		public struct _timing_suspension : IDisposable
		{
			public _timing_suspension(ChartSequencer<W> ctrl) { this.ctrl = ctrl; }
			ChartSequencer<W> ctrl;
			public void Dispose() { ctrl.begin(); }
		};
		public void begin()
		{
			lock (timings)
				timings.Push(DateTime.Now.Ticks);
		}
		public void end()
		{
			long ticks;
			lock (timings)
				ticks = timings.Pop();
			Interlocked.Add(ref tick_total, DateTime.Now.Ticks - ticks);
		}

		public void Release()
		{
			SysObj.EndApplicationTask();

			int c;
			if ((c = c_tasks) <= 0)
				return;

			c = Interlocked.Decrement(ref c_tasks);
			if (c < 0)
				throw new Exception();

			if (c == 0)
				complete_awaiters();
		}

		public bool TerminationRequested
		{
			get { return c_tasks < 0; }
		}

		public void CancelJob(Exception ex)
		{
			c_tasks = -1;
		}

		protected void StartTask<X>(Func<IScheduler, X, IDisposable> ax, X x)
			where X : class, IIdentity
		{
			if (c_tasks < 0)
				return;

			_concurrency_ext.InterlockedMax(ref c_tasks_max, Interlocked.Increment(ref c_tasks));

			var tc = TaskControl.Scheduler;

			SysObj.BeginApplicationTask();
			if (SysObj.GlobalTaskCount > 7 || tc is CurrentThreadScheduler)
			{
				ax(null, x);
			}
			else
			{
				using (suspend_timing)
					tc.Schedule<X>(x, ax);
			}
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class ChartResources<W> : ChartSequencer<W>, IUnificationController,
		ρρAgreeConfig,
		ρρChartConfig,
		ρρJobConfig,
		ρρChartStartSymbols,
		ρρGrammar
		where W : struct, IProximalContext<W>
	{

		public ChartResources(IRtParent parent, IActiveObj<IChartObj<W>> items)
			: base(parent, items)
		{
		}

		///////////////////////////////////////////////////////////////////////
		/// 
		protected AgreeConfig config;
		public AgreeConfig AgreeConfig
		{
			get { return this.config; }
			set { this.config = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		public ChartConfig cconfig;
		public ChartConfig ChartConfig
		{
			get { return this.cconfig; }
			set { this.cconfig = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		public Grammar Grammar
		{
			get { return u.Grammar; }
			set { throw not.valid; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		public StartSymbol[] rgss;
		public StartSymbol[] ChartStartSymbols
		{
			get { return rgss; }
			set { rgss = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		public QuickCheck qc;
		public QuickCheck QuickCheck
		{
			get { return this.qc; }
			set { this.qc = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////
		///
		public TfsUnifier u;
		public TfsUnifier TfsUnifier
		{
			get { return this.u; }
			set { this.u = value; }
		}

		///
		///////////////////////////////////////////////////////////////////////

		public TypeUtils tu { get { return u.tu; } }

		public Restrictors rr;

		///////////////////////////////////////////////////////////////////////
		///
		public JobConfig jc;
		public JobConfig JobConfig
		{
			get { return this.jc; }
			set { this.jc = value; }
		}
		///
		///////////////////////////////////////////////////////////////////////

#if false
		///////////////////////////////////////////////////////////////////////
		///
		public ChartDebugger ChartDebugger
		{
			get { return this._get.ChartDebugger; }
			set { this._set.ChartDebugger = value; }
		}
		public bool IsChartDebugger { get { return this.ChartDebugger != null; } }
		[Conditional("DEBUG")]
		public void bp(String name = null, params Object[] args)
		{
			var cd = this.ChartDebugger;
			if (cd != null)
				cd.bp(name, args);
		}
		///
		///////////////////////////////////////////////////////////////////////
#else
		[Conditional("DEBUG")]
		public void bp(String name = null, params Object[] args)
		{
		}
#endif
	};



	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// ChartControl - main parse/generate/packing phase
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[ContentProperty("InputTokens")]
	public abstract partial class ChartControl<T, W> : ChartResources<W>, IObserver<IChartObj<W>>, IChartControl
		where T : ChartControl<T, W>
		where W : struct, IProximalContext<W>
	{
		public ChartControl(IRtParent parent, IActiveObj<IChartObj<W>> items)
			: base(parent, items)
		{
			Action a_notify_forest;
			this.forest = new PackedForest(this, out a_notify_forest);
			((ICriticalNotifyCompletion)this).UnsafeOnCompleted(a_notify_forest);
		}

		protected IActiveRule[] rules;

		readonly PackedForest forest;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public PackedForest Forest
		{
			get
			{
				Wait();
				return forest;
			}
		}
		public int c_root { get { return forest.Count; } }

		public Tfs.Flags unify_opts;
		public int c_unif;

		protected _chart_cell spanning_cell;
		protected int spanning_count;
		public int c_tok_in, c_act, c_pass, c_span;

		public sbyte pack_opt { get; set; }
		bool f_pack_exact;
		public int c_pack_fwd, c_pack_rev, c_pack_eq;

		///////////////////////////////////////////////////////////////////////
		///
		public override int InputItemsCount
		{
			get { return c_tok_in; }
		}
		///
		///////////////////////////////////////////////////////////////////////


		public bool IsSpanning(IChartObj co) { return co.CoverageCount == spanning_count; }

		public int SpanningCount { get { return spanning_count; } }

		protected async override Task<IChartControl> start_async(IActiveObj<IChartObj<W>> toks)
		{
			begin();

			u.em.WaitForRemainingInits();

			if ((u.Restrictors.r_pack.opts & Tfs.Flags.Restrict) != 0 && (pack_opt = (sbyte)cconfig.Packing) != 0)
			{
				if (f_pack_exact = (pack_opt & (sbyte)ChartConfig.PackingOpts.Only) != 0)
					pack_opt &= (sbyte)~ChartConfig.PackingOpts.Only;

				unify_opts = Tfs.Flags.Restrict;
			}

			Initialize();

			if (spanning_count == 0)
			{
				Exception ex_inner = new ArgumentException("Parse chart cannot have zero columns", "c_columns");
				throw new TypeInitializationException(this.GetType().FullName, ex_inner);
			}

			if (rules == null || rules.Length == 0)
				throw new Exception("There are no rules");

			if (rgss == null || rgss.Length == 0)
				throw new Exception("There are no start symbols");

			end();

			toks.Subscribe(this);

			return this;
		}

		void IObserver<IChartObj<W>>.OnNext(IChartObj<W> tok)
		{
			bp("morphology result", tok);

			//Console.Out.WriteLineColor("#{0,-2} $cyan {1,5} {2:X16}$ {3,12}\r\n{4}\r\n=======",
			//	c_tok_in,
			//	tok.ProximalContext.ToString(),
			//	tok.DerivationHash,
			//	tok.GetType()._Name(),
			//	tok.TreeDisplay(true, null, null));

			//Console.Out.WriteLineColor("#{0,-2}\r\n{1}\r\n=======",
			//			c_tok_in,
			//			tok.TreeDisplay(true, null, null));

			begin();

			Interlocked.Increment(ref c_tok_in);

			tok.BeginTransact();

			StartTask(AddObj, tok);

			end();
		}

		void IObserver<IChartObj<W>>.OnCompleted()
		{
			Release();

			/// non-parse activities go here; they will not delay the delivery of the parse chart
			//if (config.parser.f_autotune)
			//    Task.Factory.StartNew(() => g.tm.Autotune());
		}

		void IObserver<IChartObj<W>>.OnError(Exception error)
		{
			CancelJob(error);
		}

		/// the following are the core hooks which differentiate the parse vs. generation charts
		protected abstract void Initialize();
		protected abstract bool CheckRuleGeometry(IActiveRule ir, ref W ctx);
		protected abstract void AddActiveEdge(ActiveEdge ae);
		protected abstract IEnumerator<IChartObj> GetPassiveForActive(ActiveEdge ae);
		protected abstract void SendToPassiveEdges(IChartObj<W> po);
		protected abstract bool CheckPassiveAccessibility(ArrayTfs new_mother, ref W ctx_new);
		protected abstract IChartObj CreateChartObj(Rule rule, ArrayTfs tfs, IReadOnlyList<IChartObj> rgo, W pxc, StartSymbol[] ss_matches);
		protected abstract void AddChartObj(IChartObj<W> po);
		protected abstract IEnumerator<IChartObj> GetPackingCandidates(IChartObj po);
		protected abstract IEnumerator<IChartObj> ItemEdges(W w);
		public abstract IEnumerable<IChartObj> AllEdges();
	};
}
